/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package es.miguelgonzalez.jgraficacomida;

import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionListener;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.swing.JPanel;

/**
 * Panel que se encarga de pintar la gráfica
 * @author Miguel González - Ceura
 */
public class JGraficaComidaDibujo extends JPanel 
        implements GraficaComidaChangeEventListener {

    /*
     * Variables que se utilizan para el pintado de la gráfica
     */
    private int posXInicial;
    private int posXFinal;
    private int posYInicial;
    private int posYFinal;
    private double escalaAltura;
    private double escalaAnchura;
    
    /*
     * Si clickedElemento es:
     * -1 Ningún punto ha sido clickado
     * 0 El primer punto está clickado
     * 1 El segundo punto está clickado
     * 2 El tercer punto está clickado
     */
    private String clickedElement = "-1";
    
    //Guardamos la referencia al modelo de la población para actualizarlo
    private JGraficaComidaModel modeloPoblacion;
    
    /**
     * Constructor de la gráfica
     * @param modeloPoblacion Recibe como parámetro un modelo de la población
     * sobre el que toma los valores para dibujarse.
     */
    public JGraficaComidaDibujo(JGraficaComidaModel modeloPoblacion) {
        super();
        
        //Guardamos el modelo de la población
        this.modeloPoblacion = modeloPoblacion;
        
        //Establecemos el tamaño por defecto de la gráfica
        setPreferredSize(new Dimension(300,200));
        
        initListeners();
    }
    
    private void initListeners() {
        //Nos añadimos como escuchadores de los eventos
        //Por si se cambia el modelo con los JTextFields
        modeloPoblacion.addChangeEventListener(this);        
        
        //Añadimos la escucha a los eventos de mover con el ratón
        addMouseMotionListener(new ManejadorRatonDrag());
        addMouseListener(new ManejadorMouseEvent());
    }
    
    /**
     * Establece el modelo de la población
     * @param modeloPoblacion Modelo de la población
     */
    public void setModelExperimento(JGraficaComidaModel modeloPoblacion) {
        //Nos quitamos de la escucha del modelo de la población
        //Para subscribirnos a escuchar el  nuevo modelo
        modeloPoblacion.removeChangeEventListener(this);

        //Guardamos el modelo de la población
        this.modeloPoblacion = modeloPoblacion;
        
        //Nos añadimos como escuchadores de los eventos
        //Por si se cambia el modelo con los JTextFields
        modeloPoblacion.addChangeEventListener(this);
        
        //Repintamos la gráfica
        repaint();
    }
    
    /**
     * Método que pinta el componente (la gráfica)
     * @param g Graphics
     */
    @Override
    public void paintComponent(Graphics g) {
        super.paintComponent(g);
        Graphics graphics = (Graphics) g.create();
        
        //Llamamos al método que pinta la gráfica según el modelo
        pintarGrafica(graphics);
    }
    
    /**
     * Calcula las posiciones donde se dibujará la gráfica respecto al
     * ancho del panel y el modelo
     */
    private void calcularPosiciones() {
        posXInicial = (int)(getWidth() * 0.10);
        posXFinal = (int)(getWidth() * 0.90);
        posYInicial = (int)(getHeight() * 0.10);
        posYFinal = (int)(getHeight() * 0.80);
        
        escalaAltura = (posYFinal - posYInicial) /
                (double)modeloPoblacion.getLimiteSuperiorComida();
        
        escalaAnchura = (posXFinal - posXInicial) / 
                (double)modeloPoblacion.getNumDias();
    }
    
    /**
     * Método que dibuja la gráfica en pantalla
     * @param g Graphics2D
     */
    public void pintarGrafica(Graphics g) {
        //Calculamos las posiciones (por si se redimensiona la gráfica)
        calcularPosiciones();
        
        //Pintamos el fondo blanco
        g.setColor(Color.WHITE);
        g.fillRect(0, 0, getWidth(), getHeight());
        
        //Pintamos los bordes de la gráfica
        g.setColor(Color.BLACK);
        
        
        //Dibujamos las líneas de la gráfica
        g.drawLine(posXInicial, //10% a la dcha
                posYInicial, //10% abajo
                posXInicial, //10% a la dcha
                posYFinal); //80% abajo
        
        g.drawLine(posXInicial, //10% a la dcha
                posYFinal, //80% abajo
                posXFinal, //90% a la dcha
                posYFinal); //80% abajo
        
        
        //Dibujamos los datos de la gráfica
        int puntoInicial = posYFinal - (int)(modeloPoblacion.getAlimentoInicial() *
                escalaAltura);
        int puntoXMaximo = posXInicial + (int)(modeloPoblacion.getDiaMax() *
                escalaAnchura);
        int puntoYMaximo = posYFinal -(int)(modeloPoblacion.getAlimentoMax() *
                escalaAltura);
        int puntoFinal = posYFinal -(int)(modeloPoblacion.getAlimentoFinal() *
                escalaAltura);
        
        //Dibujamos los círculos de los puntos
        g.setColor(Color.GRAY);
        
        g.fillOval(posXInicial - 5,
                puntoInicial - 5, 10, 10);
        
        g.fillOval(puntoXMaximo - 5,
                puntoYMaximo - 5, 10, 10);
        
        g.fillOval(posXFinal - 5,
                puntoFinal - 5, 10, 10);
        
        //Pintamos las líneas
        g.setColor(Color.CYAN);
        
        g.drawLine(posXInicial, //10% a la dcha
                puntoInicial,
                puntoXMaximo,
                puntoYMaximo);
        
        g.drawLine(puntoXMaximo, //10% a la dcha
                puntoYMaximo,
                posXFinal, //90% a la dcha
                puntoFinal);
    }

    /**
     * Al reicibir una notificación en el cambio del modelo repinta la gráfica
     * @param ev Evento producido
     */
    @Override
    public void fireChangeEvent(GraficaComidaChangeEvent ev) {
        //Para cualquier evento producido en el modelo repintamos la gráfica
        repaint();
    }

    /**
     * Manejador de los eventos de ratón al moverse
     */
    private class ManejadorRatonDrag implements MouseMotionListener {

        /**
         * Al pinchar y mover el ratón mueve uno de los puntos si puede
         * @param e Evento de ratón producido
         */
        @Override
        public void mouseDragged(MouseEvent e) {
            int max = modeloPoblacion.getLimiteSuperiorComida();
            int dias = modeloPoblacion.getNumDias();
            
            //Si está algún punto seleccionado permitimos moverlo y recalcular
            //el valor en el modelo
            if(clickedElement.equals("0")) {
                //Alimento inicial (calcular Y)
                int alimentoInicial = 0;
                try {
                    alimentoInicial = (int)((posYFinal -
                            e.getY()) / escalaAltura);
                    modeloPoblacion.setAlimentoInicial(alimentoInicial);
                } catch (GraficaComidaIncorrectValueException ex) {
                    if(alimentoInicial < 0) {
                        try {
                            modeloPoblacion.setAlimentoInicial(0);
                        } catch (GraficaComidaIncorrectValueException ex1) {
                            //No hacemos nada
                        }
                    }
                    if(alimentoInicial > modeloPoblacion.getAlimentoMax()) {
                        try {
                            modeloPoblacion.setAlimentoInicial(
                                    modeloPoblacion.getAlimentoMax());
                        } catch (GraficaComidaIncorrectValueException ex1) {
                            //No hacemos nada
                        }
                    }
                }
            } else if(clickedElement.equals("1")) {
                //Alimento máximo (Calcular X e Y)
                int alimentoDia = 0;
                int alimentoMax = 0;
                try {
                    alimentoDia = (int)((e.getX() -
                            posXInicial) / escalaAnchura);

                    modeloPoblacion.setDiaMax(alimentoDia);
                } catch (GraficaComidaIncorrectValueException ex) {
                    if(alimentoDia > modeloPoblacion.getNumDias() - 1) {
                        try {
                            modeloPoblacion.setDiaMax(
                                    modeloPoblacion.getNumDias() - 1);
                        } catch (GraficaComidaIncorrectValueException ex1) {
                            //No hacemos nada
                        }
                    } else if(alimentoDia <= 1) {
                        try {
                            modeloPoblacion.setDiaMax(1);
                        } catch (GraficaComidaIncorrectValueException ex1) {
                            //No hacemos nada
                        }
                    }
                }
                try {
                    alimentoMax = (int)((posYFinal -
                            e.getY()) / escalaAltura);
                    modeloPoblacion.setAlimentoMax(alimentoMax);
                } catch (GraficaComidaIncorrectValueException ex) {
                    if(alimentoMax > modeloPoblacion.getLimiteSuperiorComida()) {
                        try {
                            modeloPoblacion.setAlimentoMax(
                                    modeloPoblacion.getLimiteSuperiorComida());
                        } catch (GraficaComidaIncorrectValueException ex1) {
                            //No hacemos nada
                        }
                    } else if(alimentoMax < modeloPoblacion.getAlimentoInicial()
                            || alimentoMax < modeloPoblacion.getAlimentoFinal()) {
                        if(modeloPoblacion.getAlimentoInicial() <
                                modeloPoblacion.getAlimentoFinal()) {
                            try {
                                modeloPoblacion.setAlimentoMax(
                                        modeloPoblacion.getAlimentoFinal());
                            } catch (GraficaComidaIncorrectValueException ex1) {
                                //No hacemos nada
                            }
                        } else {
                            try {
                                modeloPoblacion.setAlimentoMax(
                                        modeloPoblacion.getAlimentoInicial());
                            } catch (GraficaComidaIncorrectValueException ex1) {
                                //No hacemos nada
                            }
                        }
                    }
                }
            } else if(clickedElement.equals("2")) {
                //Alimento final (calcular Y)
                int alimentoFinal = 0;
                try {
                    alimentoFinal = (int)((posYFinal -
                            e.getY()) / escalaAltura);
                    modeloPoblacion.setAlimentoFinal(alimentoFinal);
                } catch (GraficaComidaIncorrectValueException ex) {
                    if(alimentoFinal < 0) {
                        try {
                            modeloPoblacion.setAlimentoFinal(0);
                        } catch (GraficaComidaIncorrectValueException ex1) {
                            //No hacemos nada
                        }
                    }
                    if(alimentoFinal > modeloPoblacion.getAlimentoMax()) {
                        try {
                            modeloPoblacion.setAlimentoFinal(
                                    modeloPoblacion.getAlimentoMax());
                        } catch (GraficaComidaIncorrectValueException ex1) {
                            //No hacemos nada
                        }
                    }
                }
            }
        }

        /**
         * Al mover el ratón sin clickar no hace nada
         * @param e Evento de ratón producido
         */
        @Override
        public void mouseMoved(MouseEvent e) {
            //No hacemos nada
        }
        
    }
    
    /**
     * Manejador de los eventos de ratón de click
     */
    private class ManejadorMouseEvent implements MouseListener {
        /**
         * Al pulsar el ratón comprobamos si se ha pulsado sobre uno de los
         * puntos con el click izquierdo
         * @param e Evento de ratón producido
         */
        @Override
        public void mousePressed(MouseEvent e) {
            if(e.getButton() == MouseEvent.BUTTON1) {
               //Lugares donde se dibujarían los puntos de la gráfica
                int puntoInicial = posYFinal - (int)(modeloPoblacion.getAlimentoInicial() *
                        escalaAltura);
                int puntoXMaximo = posXInicial + (int)(modeloPoblacion.getDiaMax() *
                        escalaAnchura);
                int puntoYMaximo = posYFinal -(int)(modeloPoblacion.getAlimentoMax() *
                        escalaAltura);
                int puntoFinal = posYFinal -(int)(modeloPoblacion.getAlimentoFinal() *
                        escalaAltura);
                
                
                //Comprobamos si hemos clickado en el primer círculo
                if(e.getX() >= posXInicial - 5 && e.getX() <= posXInicial + 5 &&
                        e.getY() >= puntoInicial - 5 && e.getY() <= puntoInicial + 5) {
                    clickedElement = "0";
                }

                //Comprobamos si hemos clickado en el segundo círculo
                if(e.getX() >= puntoXMaximo - 5 && e.getX() <= puntoXMaximo + 5 &&
                        e.getY() >= puntoYMaximo - 5 && e.getY() <= puntoYMaximo + 5) {
                    clickedElement = "1";
                }

                //Comprobamos si hemos clickado en el tercer círculo
                if(e.getX() >= posXFinal - 5 && e.getX() <= posXFinal + 5 &&
                        e.getY() >= puntoFinal - 5 && e.getY() <= puntoFinal + 5) {
                    clickedElement = "2";
                }
            }
        }

        /**
         * Al soltar el click decimos que no hay ningún punto seleccionado
         * @param e Evento de ratón producido
         */
        @Override
        public void mouseReleased(MouseEvent e) {
            //Dejamos de poder mover los círculos
            clickedElement = "-1";
        }

        /**
         * Al salir del componente decimos que no hay ningún punto seleccionado
         * @param e Evento de ratón producido
         */
        @Override
        public void mouseExited(MouseEvent e) {
        }

        /**
         * Al hacer click no hacemos nada
         * @param e Evento de ratón producido
         */
        @Override
        public void mouseClicked(MouseEvent e) {
            //No hacemos nada
        }

        /**
         * Al entrar en el componente no hacemos nada
         * @param e Evento de ratón producido
         */
        @Override
        public void mouseEntered(MouseEvent e) {
            //No hacemos nada
        }
    }
}
